#pragma once

#include <ctime>
#include <functional>
#include <memory>
#include <queue>
#include <thread>

/// Timer type
enum class Type_ {
  NONE = 0,
  ONCE = 1,
  LOOP,
};

/// Timer callback
using TimerFunc = std::function<void(std::uint64_t)>;

/// Timer node
struct TimerNode_ {
  std::time_t trigger_time{std::time(0)};
  Type_ type{Type_::NONE};
  std::time_t duration{std::time(0)};
  bool operator<(const TimerNode_ &node) {
    return trigger_time > node.trigger_time;
  }
  std::uint64_t user_data{std::uint64_t(0)};
  TimerFunc timer_func;
  bool deleted{false};
};

using TimerHandle = std::shared_ptr<TimerNode_>;

class TimerBase {
 public:
  virtual ~TimerBase() {}
  /// Start a timer
  /// @param timer_func timer callback
  /// @param duration timeout duration
  /// @param user_data user data
  /// @return timer handle
  virtual TimerHandle addTimerOnce(TimerFunc timer_func, std::time_t duration,
                           std::uint64_t user_data) = 0;
  /// Start a loop timer, reserved
  /// @param timer_func timer callback
  /// @param duration timeout duration
  /// @param user_data user data
  /// @return timer handle
  virtual TimerHandle addTimer(TimerFunc timer_func, std::time_t duration,
                       std::uint64_t user_data) = 0;
  /// Cancel a timer, reserved
  virtual void cancelTimer(TimerHandle handle) = 0;
  /// Returns a expired timer
  /// @param now current timestamp in millionsecond
  virtual TimerHandle runOnce(std::time_t now = 0) = 0;
  /// Clear all timer
  virtual void clear() = 0;
};

/// Timer manager
class Timer : public TimerBase {
  using TimerQueue = std::priority_queue<TimerHandle>;
  TimerQueue queue_; ///< Queue map

public:
  /// ctor
  Timer();
  /// dtor
  virtual ~Timer();
  /// Start a timer
  /// @param timer_func timer callback
  /// @param duration timeout duration
  /// @param user_data user data
  /// @return timer handle
  virtual TimerHandle addTimerOnce(TimerFunc timer_func, std::time_t duration,
                           std::uint64_t user_data) override;
  /// Start a loop timer, reserved
  /// @param timer_func timer callback
  /// @param duration timeout duration
  /// @param user_data user data
  /// @return timer handle
  virtual TimerHandle addTimer(TimerFunc timer_func, std::time_t duration,
                       std::uint64_t user_data) override;
  /// Cancel a timer, reserved
  virtual void cancelTimer(TimerHandle handle);
  /// Returns a expired timer
  /// @param now current timestamp in millionsecond
  virtual TimerHandle runOnce(std::time_t now = 0) override;
  /// Clear all timer
  virtual void clear() override;

private:
  TimerHandle runOnce_(std::time_t now);
};

class TimerFactory {
 public:
  virtual ~TimerFactory() {}
  virtual TimerBase* create() = 0;
  virtual void destory(TimerBase* timer_base) = 0;
};

extern std::time_t getMilliSeconds();
extern void setTimerFactory(TimerFactory* factory);
extern TimerFactory* getTimerFactory();
